#!/usr/bin/python3
import json
from enum import Enum
import queue
import threading
from typing import List
from collections import deque

# 配置选项之间的关系，single表示多选1，combine表示组合
OptionsRelation = Enum('OptionRelation', ('single', 'combine'))
# 配置节点的类型，option代表还有子选项，value表示ConfigureOption的options值是配置的选项值
NodeType = Enum('NodeType', ('option', 'value')) 

class ConfigureOption:
    name = ''
    value = ''
    parent = None
    options = []
    optionRelation = None
    nodeType = None
    
    @classmethod
    def createWithDic(cls, dic, parent):
        parent = parent
        options = [] 
        # todo 是否需要判断name和value是否为空
        name = dic["name"]
        value = dic['value']
        
        if dic["node_type"] == "option":
            nodeType = NodeType.option
        elif dic["node_type"] == "value":
            nodeType = NodeType.value
        else:
            print(dic)  
            assert False, "上面的配置文件 node_type 字段值只能为option或者value，不能为空"
            
        if "options_relation" not in dic:
                optionRelation = None
                assert nodeType == NodeType.value      
        elif dic["options_relation"] == "single":
            optionRelation = OptionsRelation.single
        elif dic["options_relation"] == "combine":
            optionRelation = OptionsRelation.combine
        else:
            print(dic)  
            assert False, "上面的配置文件 options_relation 字段值只能为single或者combine"
            
        result = cls(name, value, nodeType, parent, None, optionRelation)
        
        # 利用递归创建一个ConfigureOption树形结构
        # todo 这里要优化一下，options和value保持对齐
        if nodeType == NodeType.option:
            for subOptionDic in dic["options"]:
                options.append(ConfigureOption.createWithDic(subOptionDic, result))
        else:
            assert nodeType == NodeType.value
            for value in dic["values"]: 
                options.append(ConfigureValue(name, value, result))
            
        result.options = options
        return result
        
    def __init__(self, name, value, nodeType, parent, options, optionRelation):
        self.name = name
        self.value = value
        self.nodeType = nodeType
        self.parent = parent
        self.options = options
        self.optionRelation = optionRelation
        return
    
class ConfigureValue:
    name = ''
    value = ''
    parent = None

    def __init__(self, name, value, parent):   
        self.parent = parent
        self.name = name
        self.value = value
        return
    
    # 生成用例的名字
    def compositeName(self):
        result = self.name
        parent = self.parent
        while parent:
            result = parent.name + " " + result
            parent = parent.parent
        return result
    
    def hasParent(self, parentName):
        parent = self
        while parent:
            if parent.name == parentName:
                return True
            parent = parent.parent
        return False    
    
    def getParent(self, parentName):
        parent = self
        while parent:
            if parent.name == parentName:
                return parent
            parent = parent.parent
        return None 
        
    def hasParentAndValue(self, parentName, value):
        parent = self.parent
        while parent:
            if parent.name == parentName:
                if parent.value == value:
                    return True
                else:
                    return False
                
            parent = parent.parent
        return False 
        
class ConfigureOptionReport:
    #存放ConfigureOption数组
    rootOption = None 
    recordID = 0
    
    def __init__(self, option):
        self.rootOption = option      
        
    def outputResults(self):
        file = open("task" + ".yaml", "w+")
        
        prefix = ""
        outputString = ""
        outputString += "- id: 1 # task_id 远程调度使用, 本地执行不同task需要不同id" + "\n"
        prefix += "    "
        outputString += prefix + "name: 串口控制拍照 # task名称, 区别不同task" + "\n"
        outputString += prefix + prefix + "- udid: COM3 # 设备id, Android和iOS需要添加, Windows只需填WindowsPC" + "\n"
        outputString += prefix + "app:" + "\n"
        outputString += prefix + prefix + "platform: Serial # 测试app平台信息" + "\n"
        outputString += prefix + prefix + "name: oner # 测试app信息" + "\n"
        outputString += prefix + prefix + "connection: socket" + "\n"
        outputString += prefix + "status: init" + "\n"
        outputString += prefix + "case:" + "\n"
        file.write(outputString)
        
        configeValeLists = self.rootConfigureOptionValues()
        for configList in configeValeLists:
            result = []
            self.outputConfigureValues(prefix + "        ", "", configList, result)
            for text in result:
                file.write(self.case(prefix, configList))
                file.write(text)
                 
        file.close()
    
    def caseName(self, configureValueList:List[ConfigureValue]) -> str:
        result = ""
        for value in configureValueList:
            result += value.name + " "
        result += str(self.recordID) 
        return result
    
    def case(self, prefix:str, configureValueList:List[ConfigureValue]) -> str:
        # 第一次调用的时候做一些通用逻辑之外的预处理，根据需要修改这里
        # 验证一下string是value类型还是引用类型
        self.recordID += 1
        result = prefix + "- id: " + str(self.recordID) + "\n"
        prefix += "  "
        caseName = self.caseName(configureValueList)
        result += prefix + "name: " + caseName + "\n"
        result += prefix + "status: init" + "\n"
        result += prefix + "ops:" + "\n"
        result += "  "
        result += prefix + "- set_picture_option:" + "\n"
        return result
    
    
    # prefix 存放输出每一行前的空格个数
    # case 存放每个case的字符串结果
    def outputConfigureValues(self, prefix: str, case: str, configureValueList: List[ConfigureOption], result: List[str]):
        # 验证configureValueList每个值都是NodeType.value类型
        for option in configureValueList:
            assert option.nodeType == NodeType.value
        
        if not configureValueList:
            if case:
                result.append(case)
            return 
            
        for value in configureValueList[0].options:
            caseCopy = case + prefix + str(value.name) + ": " + str(value.value) + "\n"
            self.outputConfigureValues(prefix, caseCopy, configureValueList[1:], result)
        
        
    def rootConfigureOptionValues(self) -> List[List[ConfigureOption]]:
        return self.configureOptionValues(self.rootOption)
        
    def configureOptionValues(self, optionNode:ConfigureOption) -> List[List[ConfigureOption]]:
        result = []
        if optionNode is None:
            return result
        
        self.dfsOutput([], optionNode, [], result)
        return result
    
    def dfsOutput(self, prefix:List[ConfigureOption], optionNode:ConfigureOption, combineOptions:List[ConfigureOption], result:List[List[ConfigureOption]]):
        #prefix 按引用传递，所以每次都要拷贝一次。
        prefixCopy = prefix[:]
        
        dequeue = deque([optionNode])
        # combineOptions is not None and len(combineOptions) > 0
        if combineOptions:
            dequeue.extend(combineOptions)
            
        while dequeue:
            node = dequeue.popleft()
            if node.nodeType == NodeType.value:
                prefixCopy.append(node)
            # 彼此独立，在这里执行路径分开，加上上一级相同的前缀
            elif node.optionRelation == OptionsRelation.single:
                for child in node.options:
                    self.dfsOutput(prefixCopy, child, list(dequeue), result)
                # 分解为上述的几个子问题，直接返回
                return
            # 组合状态，双端队列实现深度优先遍历
            else:
                assert node.optionRelation == OptionsRelation.combine
                for child in reversed(node.options):
                    dequeue.appendleft(child)
        # 这个加在这里逻辑上很对，还省了一个isLastNode参数，太漂亮了
        if prefixCopy:
            result.append(prefixCopy)
        return
        
        
        
with open('./camera_go.json', 'r') as f:
    data = json.load(f)
    
    rootOption = ConfigureOption.createWithDic(data, None)
    report = ConfigureOptionReport(rootOption)
    report.outputResults()